from django.shortcuts import render_to_response, HttpResponseRedirect
from django.template import RequestContext
from django.contrib import messages
from PIL import Image

from models import *
from django.contrib.auth.models import User
from apps.files.views.files import post_file, check_permissions
from apps.management import Management
from apps.additions.models import ModelAddition,GetAdditions,GetAdditionForms,BooleanAddition,CharAddition,IntAddition,FloatAddition,TextAddition
from apps.contact.views import send_email
from apps.course.models import Course
from apps.course.functions import course_reverse

import re
import string
import logging
from random import choice

logger = logging.getLogger("pet.apps.participants.manage")

def RandomPassword(length=10,chars=string.letters + string.digits):
	return ''.join([choice(chars) for i in range(length)])

def manage(request, TheCourseParticipant, TheCourse):
	logging.info("%s managing users",request.user)
	Users = CourseParticipant.find(TheCourse)
	return render_to_response('participants/manage.html',{'Users':Users},context_instance=RequestContext(request))
Management.register_interface('Users','ASSISTANT;INSTRUCTOR;SUPERUSER','manage',manage)

def remove(request, TheCourseParticipant, TheCourse, username):
	logger.fxn_entry('%s removing user %s', request.user.username, username)
	if not username:
		logger.debug('No username specified, returning to user management page')
		return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users'))
	
	TargetUser = User.find_or_404(username)
	TargetCourseParticipant = CourseParticipant.find_or_404(TheCourse, TargetUser)
	if request.user.is_superuser or TheCourseParticipant.status == "INSTRUCTOR" or (TheCourseParticipant.status == "ASSISTANT" and TargetCourseParticipant.status == "STUDENT"):
		TargetCourseParticipant.delete()
		messages.info(request,"%s removed from %s" % (TargetCourseParticipant.user,TheCourse))
		logger.info("%s removed from course %s",TargetCourseParticipant.user,TheCourse.course_id)
	else:
		logger.debug("%s not allowed to remove %s from course", request.user, TargetUser)
		messages.info(request,"You do not have the required priveleges in this course to remove this user")
	return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users'))
Management.register_interface('Users','ASSISTANT;INSTRUCTOR;SUPERUSER','remove',remove)

def edit(request, TheCourseParticipant, TheCourse, username=None):
	logger.fxn_entry('%s editing user %s', request.user, username)
	HasError = False
	NewUser = None
	TargetUser = None
	TargetCourseParticipant = None
	if username:
		TargetUser = User.find_or_404(username)
		TargetCourseParticipant = CourseParticipant.find_or_404(TheCourse, TargetUser)
	
	if request.method == 'POST':
		CourseForm = CourseParticipantForm(request.POST, request.FILES, instance=TargetCourseParticipant)
		UserInfoForm = ManagementUserForm(request.POST,request.FILES, instance=TargetUser)
		AdditionForms = GetAdditionForms(model='User', instanceid=User, course=TheCourse.course_id, postinfo=request.POST)
		if UserInfoForm.is_valid():
			if UserInfoForm.has_changed():
				if not TargetUser:
					NewUserPW = RandomPassword()
					UserInfoForm.instance.set_password(NewUserPW)
				NewUser = UserInfoForm.save()
				if not TargetUser:
					if UserInfoForm.cleaned_data['email_users']:
						subject = "Welcome to the %s Website" % TheCourse
						message = """You have been registered on the %s website.
									\nYour registration information is:\n
									\n   username: %s
									\n   password: %s
									\n
									\nThanks!
									\n%s""" % (TheCourse, NewUser.username, NewUserPW,TheCourse)
						send_email(UserInfoForm.instance, subject, message,course=TheCourse)
			else: logger.form_unchanged("UserInfoForm unchanged")
		else: HasError = True; logger.error("UserInfoForm invalid - %s",UserInfoForm)

		if NewUser and not TargetUser:
			CourseForm.instance.user = NewUser
			CourseForm.save()
		elif CourseForm.is_valid():
			if CourseForm.has_changed() and not HasError:
				CourseForm.save()
			else: logger.form_unchanged("CourseForm unchanged or error reported above")
		else: HasError = True; logger.debug("CourseForm invalid")
		
		if AdditionForms:
			for addition,form in AdditionForms:
				if NewUser and not TargetUser:
					form.instance.instanceid = NewUser
					form.save()
				elif form.is_valid():
					form.instance.instanceid = TargetUser
					if form.has_changed():
						form.save()
					else: logger.form_unchanged("Addition form for %s unchanged",addition.name)
				else: HasError = True; logger.debug("Addition form for %s invalid",addition.name)

		if 'file' in request.FILES:
			logger.info("Avatar upload")
			#Check if file is good
			Pass, Reason = check_permissions(request,using=None,course=TheCourse.course_id)
			if not Pass:
				return Reason
			
			#Write the file to temp storage
			file = request.FILES['file']
			from django.core.files import temp as tempfile
			temp = tempfile.NamedTemporaryFile(suffix=".tmp")
			for chunk in file.chunks():
				temp.write(chunk)
			temp.seek(0)
			
			#Open in PIL and check if it meets avatar reqs
			image = Image.open(temp)
			width,height = image.size
			from options import Options
			max_height = Options.get_option("Max Avatar Height",200.0)
			max_width = Options.get_option("Max Avatar Width", 300.0)
			
			#If not, shrink it and save it back to request.FILES
			if (height > max_height or width > max_width):
				aspect_ratio = float(width)/float(height)
				if (max_height/height > max_width/width):
					new_height = max_width/aspect_ratio
					new_width = max_width
				else:
					new_width = max_height*aspect_ratio
					new_height = max_height
				logger.debug("Shrinking image from %dx%d to %dx%d",width,height,new_width,new_height)
					
				image.thumbnail((int(new_width),int(new_height)),Image.ANTIALIAS)
				new_width,new_height=image.size
				from django.core.files.uploadhandler import TemporaryUploadedFile
				request.FILES['file'] = TemporaryUploadedFile(name="avatar.png", content_type=file.content_type, charset=file.charset, size=new_width*new_height*4)
				image.save(request.FILES['file'].file,format='PNG')
			request.FILES['file'].name = "avatar.png"
			
			#Delete temp file
			temp.close()
			
			#Save file
			logger.info("Saving uploaded file via post_file")
			FileResponse = post_file(request)
			if (FileResponse.status_code != 302): return FileResponse
		
		if not HasError and NewUser and not TargetUser and 'Save' in request.POST and request.POST['Save'] == 'SaveEdit':
			logger.info("SaveEdit: returning to user editing page")
			return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users',command='edit', arguments=NewUser.username))
		if not HasError and 'Save' in request.POST and request.POST['Save'] == 'SaveReturn':
			logger.info("SaveReturn: returning to user management page")
			return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users'))
	else:
		CourseForm = CourseParticipantForm(instance=TargetCourseParticipant,initial={'course':TheCourse})
		UserInfoForm = ManagementUserForm(instance=TargetUser)
		AdditionForms = GetAdditionForms(model='User', instanceid=TargetUser, course=TheCourse.course_id)

	OtherUsers = Usernames = []
	Users = CourseParticipant.find(TheCourse)
	if Users: Usernames = [user.user.username for user in Users]
	for user in sorted(User.objects.all(),key=lambda user: user.last_name):
		if not user.username in Usernames: OtherUsers.append(user)
	return render_to_response('participants/edit.html',{'course_form':CourseForm,'user_form':UserInfoForm,
														'addition_forms':AdditionForms, 'OtherUsers':OtherUsers,
														'TheUser':TargetCourseParticipant},context_instance=RequestContext(request))
Management.register_interface('Users','ASSISTANT;INSTRUCTOR;SUPERUSER','edit',edit)

def add(request, TheCourseParticipant, TheCourse, username):
	logger.info("%s adding user %s to course %s",request.user,username,TheCourse.course_id)
	TargetUser = User.find_or_404(username)
	TargetCourseParticipant = CourseParticipant.find(user=TargetUser,course=TheCourse)
	
	if (not request.user.is_superuser) and ((not TheCourseParticipant) or (TheCourseParticipant.status != "INSTRUCTOR" and TheCourseParticipant.status != "ASSISTANT")):
		logger.info("CourseParticipant %s (%s) doesn't have sufficient authority to add a user",TheCourseParticipant, request.user)
		messages.info(request,"Access to this part of the site requires authentication"); 
		return HttpResponseRedirect(course_reverse('course_index',TheCourse))

	if not TargetCourseParticipant:
		TargetCourseParticipant = CourseParticipant(user=TargetUser,course=TheCourse,status="STUDENT")
		TargetCourseParticipant.save()
		logger.info("User %s added to course %s as student",TargetCourseParticipant,TheCourse.course_id)
	#Return the user to the add user screen (at the bottom of edit)
	return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users',command='edit') + '#add_user_list')
Management.register_interface('Users','ASSISTANT;INSTRUCTOR;SUPERUSER','add',add)

def upload(request, TheCourseParticipant, TheCourse):
	logger.fxn_entry("%s uploading users to %s",request.user,TheCourse.course_id)

	ProcessedHeaders = None
	ProcessedData = None
	Errors = None
	if request.method == 'POST':
		Form = MultiUserUploadForm(request.POST, request.FILES)
		
		if Form.is_valid():
			#Process text
			separator = Form.cleaned_data['separator'] if Form.cleaned_data['separator'] != 'OTHER' else Form.cleaned_data['other_separator']
			text = Form.cleaned_data['data']
			text_data = None
			headers = None
			if text: text_data = [line.split(separator) for line in text.strip().replace('\r','').split('\n')]
			
			if Form.cleaned_data['headers_included'] and text_data:
				headers = text_data[0]
				text_data = text_data[1:]
			else:
				if Form.cleaned_data['name_format'] == 'Same': headers = ['Name', 'Email']
				elif Form.cleaned_data['name_format'] == 'SeparateLastFirst': headers = ['Last name', 'First name', 'Email']
				elif Form.cleaned_data['name_format'] == 'SeparateFirstLast': headers = ['First name', 'Last name', 'Email']
			
			if text_data:
				ProcessedHeaders, ProcessedData, Errors = ProcessUploadData(TheCourse.course_id, headers, text_data)
			else:
				Errors = ["No data was entered. Is 'Headers Included' accidentally checked?"]
			if 'Save' in request.POST and request.POST['Save'] == 'Create' and not Errors:
				#User has approved, create each new user
				#columns -> ['Ok?', 'New', 'Last name', 'First name', 'Username', 'Email Address' 'Addition1'...]
				logger.info("Generating users from uploaded data...")
				CurrentUsernames = [user.username for user in User.objects.all()]
				for user in ProcessedData:
					NewUser = None
					if user[4] in CurrentUsernames:
						NewUser = User.objects.filter(username=user[4])[0]
					else:
						NewUser = User(username=user[4],first_name=user[3],last_name=user[2],email=user[5],is_active=True)
						NewUserPW = RandomPassword()
						NewUser.set_password(NewUserPW)
						logger.info("Saving NewUser %s",NewUser)
						NewUser.save()
					if Form.cleaned_data['email_users']:
						subject = "Welcome to the %s Website" % TheCourse.course_title
						message = """You have been registered on the %s website.
									\nYour registration information is:\n
									\n   username: %s
									\n   password: %s
									\n
									\nThanks!
									\n%s""" % (TheCourse, NewUser.username, NewUserPW,TheCourse)
						send_email(NewUser, subject, message,course=TheCourse)

					
					NewCourseParticipant = CourseParticipant.find(user=NewUser,course=TheCourse)
					if not NewCourseParticipant:
						NewCourseParticipant = CourseParticipant(user=NewUser,course=TheCourse,status="STUDENT")
    					logger.info("Saving NewCourseParticipant %s",NewCourseParticipant)
    					NewCourseParticipant.save()
						
					Additions = ModelAddition.find(TheCourse.course_id,model='Participant')
					if Additions:
						AdditionList = GetAdditions(course=TheCourse.course_id,model='Participant')
						logger.debug("AdditionList: %s",AdditionList)
						AdditionNames = [x.name for x in Additions]
						
						#Check each addition that we should add:
						# - make sure it is a valid addition
						# - if it already exists, skip it
						# - create it
						for addition_index,addition in enumerate(ProcessedHeaders[6:]):
							addition_index = addition_index + 6
							if addition in AdditionNames:
								AdditionToCreate = ModelAddition.find(TheCourse.course_id,model='Participant',name=addition)
								if AdditionToCreate:
									if not {
											'BOOL': BooleanAddition,
											'CHAR': CharAddition,
											'TEXT': TextAddition,
											'INT': IntAddition,
											'FLOAT': FloatAddition,
										}[AdditionToCreate.type].objects.filter(modeladdition=AdditionToCreate,instanceid=NewUser):
										#Doesn't exist, create it
										Value = {
												'BOOL': BooleanAddition,
												'CHAR': CharAddition,
												'TEXT': TextAddition,
												'INT': IntAddition,
												'FLOAT': FloatAddition,
										}[AdditionToCreate.type](modeladdition=AdditionToCreate,instanceid=NewUser,value=user[addition_index])
										Value.save()
									
				return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='users'))
		else:
			logger.info("CourseForm invalid")
	else:
		Form = MultiUserUploadForm()
		Form.fields['email_users'].widget = forms.HiddenInput()

	return render_to_response('participants/upload.html',{'form':Form, 'ProcessedHeaders':ProcessedHeaders, 'ProcessedData':ProcessedData, 'Errors':Errors},context_instance=RequestContext(request))
Management.register_interface('Users','ASSISTANT;INSTRUCTOR;SUPERUSER','upload',upload)
	
def ProcessUploadData(course, headers, data):
	first_name = None
	last_name = None
	name = None
	username = None
	email = None
	additions = {}
	AdditionList = ModelAddition.find(course,model='Participant')
	CurrentUsernames = [user.username for user in User.objects.all()]
	for index,item in enumerate(headers):
		if not first_name and re.search(r"first\s?(name)?",item,re.IGNORECASE):
			first_name = index
		elif not last_name and re.search(r"last\s?(name)?",item,re.IGNORECASE):
			last_name = index
		elif not username and re.search(r"user\s?(name)?",item,re.IGNORECASE):
			username = index
		elif not email and re.search(r"e\-?mail",item,re.IGNORECASE):
			email = index
		elif not name and re.search(r"name",item,re.IGNORECASE):
			name = index
		if AdditionList:
			for addition_index,addition in enumerate(AdditionList):
				if not addition.name in additions and re.search(addition.name,item,re.IGNORECASE):
					additions[addition.name] = index
	
	ReturnHeaders = ['OK?', 'New', 'Last name', 'First name', 'Username', 'Email Address']
	for addition in additions.keys():
		ReturnHeaders.append(addition)
		
	ReturnData = []
	Errors = []
	ArraySize = max([first_name,last_name,name,username,email]+additions.values())+1
	for row in data:
		if len(row) < ArraySize:
			row = row + ['']*(ArraySize-len(row))
		user_first = None
		user_last = None
		user_name = None
		email_address = None
		new_user = True
		error_found = False
		ReturnRow = []
		
		#Name
		if last_name != None and first_name != None:
			user_first = row[first_name]
			user_last = row[last_name]
		elif name != None:
			Name = [x.strip() for x in row[name].split(',')]
			if len(Name) > 1:
				user_first = Name[1]
				user_last = Name[0]
			else:
				Name = [x.strip() for x in row[name].split(' ')]
				if len(Name) > 1:
					user_first = Name[0]
					user_last = Name[1]
		else:
			Errors.append('Unable to find name for user')
			error_found = True
		
		#Check if user with first name and last name exists
		if user_first and user_last:
			user = User.objects.filter(first_name=string.capwords(user_first),last_name=string.capwords(user_last))
			if user:
				user_name = user[0].username
				email_address = user[0].email
				new_user = False
		
		#Username
		if not user_name and username != None and row[username]: user_name = row[username]
		elif not user_name and user_first and user_last:
			user_name = ''.join([x.strip()[0] for x in user_first.split(' ')]) + user_last
		elif not user_name:
			Errors.append('Cannot find or create username for user %s %s' % (user_first, user_last))
			error_found = True
			
		#Check if username exists
		if user_name and user_name in CurrentUsernames:
			user = User.objects.filter(username=user_name)[0]
			if user.first_name.lower() != user_first.lower() or user.last_name.lower() != user_last.lower():
				Errors.append("""Username %s already in use by another user. Please specify a username
							 for %s %s that is different (either by adding a username column to the data
							 or changing the username column value for this user)""" % (user_name, user_first, user_last))
				error_found = True
			else: new_user = False
			
		#Email
		if not email_address and email != None:
			email_address = row[email]
		if not email_address:
			Errors.append("No email address given for %s %s" % (user_first, user_last))
			error_found = True
		
		ReturnRow.append(error_found)
		ReturnRow.append(new_user)
		ReturnRow.append(string.capwords(user_last) if user_last else '')
		ReturnRow.append(string.capwords(user_first) if user_first else '')
		ReturnRow.append(user_name or '')
		ReturnRow.append(email_address or '')
		
		for addition in additions.values():
			ReturnRow.append(row[addition])
			
		ReturnData.append(ReturnRow)
		
	return ReturnHeaders, ReturnData, Errors

	
